/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

/**
 *  promise.all()//将多个promise放入，返回按照顺序放入的promise的值，若是一个执行失败则都失败
 *  promise.prototype.catch()//失败捕获
 *  promise.prototype.finally()//不管成功或者失败都要执行，非静态方法，实例调用
 *  promise.resolve();
 *  reject()//失败执行
 *  resolve()//成功执行
 *  prototype.then()//回调函数，参数可选，可有多个，可链式调用
 *  异步处理
 *  健壮性
 */

const PENDING = 'pending' //状态
const FULFILLED = 'fulfilled'//成功
const REJECTED = 'rejected' //失败

class MyPromise {
    //构造函数，执行的时候直接执行执行器
    constructor(executor) {
        //捕获是为了程序健壮性
        try{
            executor(this.resolve, this.reject)
        }catch(e){
            this.reject(e);
        }
    }

    status = PENDING;//设置初始状态为等待
    value = undefined;//成功之后的数值
    reason = undefined;//失败之后的原因
    successCallback = [];//用于异步处理，用于存储then传入的方法，可有多个
    failCallback = [];//用户异步处理，用于存储then传入的方法，可有多个

    /**
     * 成功函数
     * @param value
     * 若是写为resolve(value){} 则在函数中取不到this的值
     */
    resolve = value => {
        //将状态改为成功，但是需要判断是否是等待状态，满足状态一旦改变不可再更改的条件
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value;
        //用while是为了满足当前这个promise的then被同时调用的时候都会执行
        while (this.successCallback.length) this.successCallback.shift()(value);
    }

    /**
     * 失败函数
     * @param reason
     */
    reject = reason => {
        //将状态改为失败，但是需要判断是否是等待状态，满足状态一旦改变不可再更改的条件
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason;
        //用while是为了满足当前这个promise的then被同时调用的时候都会执行
        while (this.failCallback.length) this.failCallback.shift()(reason);
    }

    //then 方法有两个回调函数作为参数，第一个是成功的回调函数，第二个是失败的回调函数
    then(successCallback, failCallback) {
        // 参数可选
        successCallback = successCallback ? successCallback : value => value;
        failCallback = failCallback ? failCallback : reason => { throw reason };
        //为了实现链式调用，因此此处应再返回一个promise
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                //因为我们的promise2会在当前执行完才会获得到，所以我们使用resolcePromise
                //传递的时候会获取不到，因此这里设置一个异步
                setTimeout(()=>{
                   try{
                       let x = successCallback(this.value);
                       /**
                        * 判断x的值是普通值还是promise对象
                        * 如果是普通值 直接调用resolve
                        * 如果是promise对象查看promise对象返回的结果
                        * 在根据promise对象返回的结果 决定调用resolve还是调用reject
                        */
                       resolvePromise(promise2, x, resolve, reject);
                   }catch (e) {
                       reject(e);
                   }
                },0)
            } else if (this.status === REJECTED) {
                setTimeout(()=> {
                    try{
                        let x = failCallback(this.reason);
                        /**
                         * 判断 x 的值是普通值还是promise对象
                         * 如果是普通值 直接调用resolve
                         * 如果是promise对象 查看promsie对象返回的结果
                         * 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                         */
                        resolvePromise(promise2, x, resolve, reject)
                    }catch (e) {
                        reject(e)
                    }
                },0)
            } else if (this.status === PENDING) {
                //将成功回调以及失败回调存储起来实现异步
                //回调再放入一个方法就可以进行回调、健壮等操作，实现异步
                this.successCallback.push(()=>{
                    setTimeout(()=> {
                        try{
                            let x = successCallback(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        }catch (e) {
                            reject(e);
                        }
                    },0)
                });
                this.failCallback.push(()=>{
                    setTimeout(()=> {
                        try{
                            let x = failCallback(this.reason);
                            resolvePromise(promise2, x, resolve, reject)
                        }catch(e){
                            reject(e);
                        }
                    },0)
                });
            }
        })
        return promise2;
    }

    //可以在不管结果是成功还是失败，都可以执行，在其后面可以链式调用then方法
    finally(callback){
        //用then得到当前promise的状态
        //finally后面链式调用then方法，所以可以返回一个调用then的方法则可以在finally后面再次调用
        return this.then(value=>{
            // callback();  MyPromise.resolve方法就是为了等待finally里面的promise方法执行结束再返回
            return MyPromise.resolve(callback()).then(()=>value)
        },reason=>{
            // callback();  同上
            return MyPromise.resolve(callback()).then(()=> {throw reason})
        })
    }

    //获取异常，模拟then方法，将成功回调函数传递为undefined
    catch (failCallBack) {
        return this.then(undefined, failCallBack);
    }

    //将给定的值返回，并被promise包裹
    static resolve(value){
        if (value instanceof MyPromise){
            return value;
        }else{
            return new MyPromise( (resolve,reject)=>{ resolve(value);
            })
        }
    }

    //返回数据保持顺序
    //接受数组
    //返回一个promise对象
    //可传入promise或者普通值
    //有一个失败则直接失败
    static all(array){
        let result = [];
        let index = 0;//处理有一些异步请求
        return new MyPromise((resolve,reject)=>{
            function addData (key,value) {
                result[key] = value;
                index++;
                if (index === array.length){
                    resolve(result);//保证每一项都执行结束
                }
            }

            for (let i = 0 ; i< array.length ; i++){
                if (array[i] instanceof MyPromise){
                    //若是promise，则先执行
                    array[i].then(value=> addData(i,value),  reason=> reject(reason))
                }else{
                    //普通值直接放入
                    addData(i,array[i]);
                }
            }
        })
    }
}

function resolvePromise(promise2, x, resolve, reject){
    if (promise2 === x){
        //禁止x与自身循环回调
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (x instanceof MyPromise){
        //若x本身是一个promise对象
        x.then(resolve,reject)
    }else{
        resolve(x);
    }
}

module.exports = MyPromise;